<?php
namespace App\Libs\ZiNiao\Common;
use App\Libs\ZiNiao\Request\BaseRequest;

class OpenClient
{
    private $appId;
    private $privateKey;
    private $url;

    private $charset = "UTF-8";

    private $timeout = 10;// 超时时间

    public $debugInfo = false;

    const HEADERS = 'Accept-Encoding: identity';
    const CONTENT_TYPE_APPLICATION_FORM_URLENCODED = "Content-type: application/x-www-form-urlencoded";
    const CONTENT_TYPE_APPLICATION_JSON = "Content-Type: application/json";

    /**
     * 初始化客户端
     * @param string $appId 应用ID
     * @param string $privateKey 应用私钥，2048位，PKCS8
     * @param string $url 请求url
     */
    public function __construct($appId, $privateKey, $url)
    {
        $this->appId = $appId;
        $this->privateKey = $privateKey;
        $this->url = $url;
    }

    /**
     * @param int $timeout 请求超时时间
     */
    public function setTimeout($timeout)
    {
        $this->timeout = $timeout;
    }

    /**
     * @param bool $debugInfo 是否打印debug日志
     */
    public function setDebugInfo($debugInfo)
    {
        $this->debugInfo = $debugInfo;
    }

    public function execute($request)
    {
        return $this->doExecute($request, null, null);
    }

    public function executeUserToken($request, $userToken)
    {
        return $this->doExecute($request, $userToken, null);
    }

    public function executeAppToken($request, $appToken)
    {
        return $this->doExecute($request, null, $appToken);
    }

    private function doExecute($request, $userToken, $appToken)
    {
        $totalParams = $this->buildParams($request, $userToken, $appToken);
        $requestType = $request->getRequestType();
        if ($requestType == RequestTypes::GET) {
            if (is_string($totalParams)) {
                $url = $this->url . (strpos($this->url, '?') === false ? '?' : '') . $totalParams;
            } else {
                $url = $this->url . (strpos($this->url, '?') === false ? '?' : '') . http_build_query($totalParams);
            }
        } else if ($requestType == RequestTypes::POST_FORM) {
            $url = $this->url;
            if (!is_string($totalParams)) {
                $totalParams = http_build_query($totalParams);
            }
        } else {
            $url = $this->url;
            if (!is_string($totalParams)) {
                $totalParams = json_encode($totalParams);
            }
        }
        return $this->send_request($url, $totalParams, $requestType);
    }

    private function send_request($url, $data, $requestType)
    {
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_HEADER, false);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout);
        switch ($requestType) {
            case RequestTypes::GET:
                curl_setopt($ch, CURLOPT_HTTPHEADER, array($this::HEADERS, $this::CONTENT_TYPE_APPLICATION_FORM_URLENCODED));
                break;
            case RequestTypes::POST_JSON:
                curl_setopt($ch, CURLOPT_POST, true);
                curl_setopt($ch, CURLOPT_HTTPHEADER, array($this::HEADERS, $this::CONTENT_TYPE_APPLICATION_JSON));
                curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
                break;
            case RequestTypes::POST_FORM:
                curl_setopt($ch, CURLOPT_POST, true);
                curl_setopt($ch, CURLOPT_HTTPHEADER, array($this::HEADERS, $this::CONTENT_TYPE_APPLICATION_FORM_URLENCODED));
                curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
                break;
            case RequestTypes::PUT:
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PUT");
                curl_setopt($ch, CURLOPT_HTTPHEADER, array($this::HEADERS, $this::CONTENT_TYPE_APPLICATION_JSON));
                curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
                break;
            case RequestTypes::DELETE:
                curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "DELETE");
                curl_setopt($ch, CURLOPT_HTTPHEADER, array($this::HEADERS, $this::CONTENT_TYPE_APPLICATION_JSON));
                curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
                break;
            default:
                throw new \Exception('$request->getRequestType()类型不正确');
        }

        $ret = curl_exec($ch);
        if ($this->debugInfo) {
            $info = curl_getinfo($ch);
            $contents = array(
                'httpInfo' => array(
                    'send' => $data,
                    'url' => $url,
                    'ret' => $ret,
                    'http' => $info,
                )
            );
            print_r($contents);
        }
        curl_close($ch);
        return $ret;
    }

    /**
     * 构建请求参数
     * @param BaseRequest $request
     * @param $userToken
     * @param $appToken
     * @return array
     */
    private function buildParams(BaseRequest $request, $userToken, $appToken)
    {
        $allParams["app_id"] = $this->appId;
        $allParams["method"] = $request->getMethod();
        $allParams["charset"] = $this->charset;
        $allParams["sign_type"] = "RSA2";
        $allParams["timestamp"] = self::getMillisecond();
        $allParams["version"] = $request->getVersion();
        $allParams["sdk_version"] = $request->getSdkVersion();
        $allParams["biz_content"] = $request->getBizContent();
        $allParams["params_content"] = $request->getParamsContent();
        if ($userToken) {
            $allParams["user_access_token"] = $userToken;
        }
        if ($appToken) {
            $allParams["app_auth_token"] = $appToken;
        }
        ksort($allParams);
        //签名
        $allParams["sign"] = SignUtil::generateSign($allParams, $this->privateKey, "RSA2");
        return $allParams;
    }

    /**
     * 获取时间戳到毫秒
     * @return bool|string
     */
    public static function getMillisecond()
    {
        list($msec, $sec) = explode(' ', microtime());
        $microTime = (float)sprintf('%.0f', (floatval($msec) + floatval($sec)) * 1000);
        return substr($microTime, 0, 13);
    }

    function echoDebug($content)
    {
        if ($this->debugInfo) {
            echo "<br/>" . $content;
        }
    }

}
